package org.oxenframework.mybatis.generator;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.oxenframework.mybatis.generator.config.SourcegeneratorProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;

@Component
@EnableConfigurationProperties(SourcegeneratorProperties.class)
public class SourceGenerator {

	@Autowired
	private SourcegeneratorProperties sourcegeneratorProperties;

	public void createApiInterfaceSource() throws SQLException, IOException {
		String connectionUrl = "jdbc:mysql://" + sourcegeneratorProperties.getDbHost() + ":"
				+ sourcegeneratorProperties.getDbPort() + "/" + sourcegeneratorProperties.getDbName() + "?user="
				+ sourcegeneratorProperties.getDbUser() + "&password=" + sourcegeneratorProperties.getDbPassword();
		Connection conn = (Connection) DriverManager.getConnection(connectionUrl);
		Statement stateMent = (Statement) conn.createStatement();
		ResultSet colSet = stateMent
				.executeQuery("select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='"
						+ sourcegeneratorProperties.getDbName() + "'");
		List<String> tableList = new ArrayList<String>();
		while (colSet.next()) {
			tableList.add(colSet.getString("TABLE_NAME"));
		}
		stateMent.close();
		if(tableList!=null){
			for (String tableName : tableList) {
				createDAO(tableName);
			}
		}
		System.out.printf("OK");
	}

	private void createDAO(String tableName) throws SQLException, IOException {
		Map<String, Object> data = new HashMap<String, Object>();
		String className = toHeadUpper(dbNameToPropertyName(tableName, true));
		String smallClassName = toHeadLow(dbNameToPropertyName(tableName, true));
		data.put("className", className);
		data.put("smallClassName", smallClassName);
		data.put("tableName", tableName);
		data.put("tableChineseName", tableName);
		data.put("authorName", sourcegeneratorProperties.getAuthorName());
		data.put("dtoPath", sourcegeneratorProperties.getModelPackage());
		data.put("apiInterfacePath", sourcegeneratorProperties.getMapperPackage());
		data.put("idPropertyName", smallClassName + "Id");
		data.put("bigIdPropertyName", className + "Id");
		String idColumnName = tableNameToIdColumnName(tableName);
		data.put("idColumnName", idColumnName);
		data.put("apiPostfix", sourcegeneratorProperties.getApiPostfix());

		String connectionUrl = "jdbc:mysql://" + sourcegeneratorProperties.getDbHost() + ":"
				+ sourcegeneratorProperties.getDbPort() + "/" + sourcegeneratorProperties.getDbName() + "?user="
				+ sourcegeneratorProperties.getDbUser() + "&password=" + sourcegeneratorProperties.getDbPassword();
		Connection conn = (Connection) DriverManager.getConnection(connectionUrl);
		Statement stateMent = (Statement) conn.createStatement();
		ResultSet colSet = stateMent.executeQuery(
				"select * from information_schema.COLUMNS where TABLE_NAME='" + tableName
						+ "' and TABLE_SCHEMA='" + sourcegeneratorProperties.getDbName() + "'");
		List<HashMap<String, String>> columnList = new ArrayList<HashMap<String, String>>();
		List<HashMap<String, String>> addColumnList = new ArrayList<HashMap<String, String>>();
		List<HashMap<String, String>> updateColumnList = new ArrayList<HashMap<String, String>>();
		while (colSet.next()) {

			HashMap<String, String> column = new HashMap<String, String>();
			String columnName = colSet.getString("COLUMN_NAME");
			String columnType = colSet.getString("DATA_TYPE");
			String columnComment = colSet.getString("COLUMN_COMMENT");
			String columnKey= colSet.getString("COLUMN_KEY");

			String propertyName = dbNameToPropertyName(columnName);
			column.put("propertyName", propertyName);
			column.put("columnName", columnName);
			column.put("bigPropertyName", toHeadUpper(propertyName));
			column.put("columnComment", columnComment);
			column.put("columnKey", columnKey);
			
			if (columnType.equals("bigint")) {
				column.put("propertyType", "Long");
				data.put("hasLong", true);
			} else if (columnType.equals("datetime")) {
				column.put("propertyType", "Date");
				data.put("hasDate", true);
			} else if (columnType.equals("decimal")) {
				column.put("propertyType", "BigDecimal");
				data.put("hasDecimal", true);
			} else if (columnType.equals("int")) {
				column.put("propertyType", "Integer");
				data.put("hasInteger", true);
			} else if (columnType.equals("tinyint")) {
				String type = colSet.getString("COLUMN_TYPE");
				if (type.equals("tinyint(1)")) {
					column.put("propertyType", "Boolean");
					data.put("hasBoolean", true);
				} else {
					column.put("propertyType", "Integer");
					data.put("hasInteger", true);
				}
			} else if (columnType.equals("varchar")) {
				column.put("propertyType", "String");
				data.put("hasString", true);
			} else if (columnType.equals("varbinary")) {
				column.put("propertyType", "String");
				data.put("hasString", true);
			} else if (columnType.equals("char")) {
				column.put("propertyType", "String");
				data.put("hasString", true);
			} else if (columnType.equals("longblob")) {
				column.put("propertyType", "byte[]");
			} else if (columnType.equals("double")) {
				column.put("propertyType", "Double");
				data.put("hasDouble", true);
			} else {
				throw new RuntimeException("未知数据类型");
			}
			columnList.add(column);
			if (!columnName.equals(idColumnName) && !columnName.equals("gmt_modify")
					&& !columnName.equals("gmt_create")) {
				addColumnList.add(column);
			}

			if (!columnName.equals(idColumnName) && !columnName.equals("gmt_modify") && !columnName.equals("gmt_create")
					&& !columnName.equals(sourcegeneratorProperties.getShardingField())) {
				updateColumnList.add(column);
			}

			if (columnName.equals(sourcegeneratorProperties.getQueryField())) {
				data.put("queryField", column);
			}

			if (columnName.equals(sourcegeneratorProperties.getQueryListField())) {
				data.put("queryListField", column);
				data.put("serviceHasList", true);
			}

			if (columnName.equals(sourcegeneratorProperties.getUpdateField())) {
				data.put("updateField", column);
			}

			if (columnName.equals(sourcegeneratorProperties.getRemoveField())) {
				data.put("removeField", column);
			}

			if (columnName.equals(sourcegeneratorProperties.getShardingField())) {
				data.put("shardingField", column);
			}
		}
		stateMent.close();

		if (data.get("shardingField") != null) {
			if ((data.get("queryField") != null) && data.get("queryField") != data.get("shardingField")) {
				data.put("queryFieldNotShardingField", true);
			}
			if ((data.get("queryListField") != null) && data.get("queryListField") != data.get("shardingField")) {
				data.put("queryListFieldNotShardingField", true);
			}
			if ((data.get("updateField") != null) && data.get("updateField") != data.get("shardingField")) {
				data.put("updateFieldNotShardingField", true);
			}
			if ((data.get("removeField") != null) && data.get("removeField") != data.get("shardingField")) {
				data.put("removeFieldNotShardingField", true);
			}
		}

		if (columnList.size() < 1) {
			throw new RuntimeException("数据表不存在");
		}

		data.put("columnList", columnList);
		data.put("addColumnList", addColumnList);
		data.put("updateColumnList", updateColumnList);

		Properties properties = System.getProperties();
		properties.put("resource.loader", "class");
		properties.put("class.resource.loader.class",
				"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		properties.put(Velocity.ENCODING_DEFAULT, sourcegeneratorProperties.getEncoding());
		properties.put(Velocity.INPUT_ENCODING, sourcegeneratorProperties.getEncoding());
		properties.put(Velocity.OUTPUT_ENCODING, sourcegeneratorProperties.getEncoding());
		properties.put("runtime.log", "target/velocity.log");
		VelocityEngine velocityEngine = new VelocityEngine(properties);

		String dtoString = mergeTemplateIntoString(velocityEngine,
				"/org/oxenframework/mybatis/generator/template/entify.vm", data);
		String apiInterfaceString = mergeTemplateIntoString(velocityEngine,
				"/org/oxenframework/mybatis/generator/template/daoInterface.vm", data);

		String replaceStr = File.separator;
		if (replaceStr.equals("\\"))
			;
		replaceStr = "\\\\";

		String dtoDirPath = sourcegeneratorProperties.getSrcPath() + File.separator
				+ sourcegeneratorProperties.getModelPackage().replaceAll("\\.", replaceStr);
		String apiInterfaceDirPath = sourcegeneratorProperties.getSrcPath() + File.separator
				+ sourcegeneratorProperties.getMapperPackage().replaceAll("\\.", replaceStr);

		if (dtoDirPath.length() != 0) {
			File dbMapFileDir = new File(dtoDirPath);
			if (!dbMapFileDir.exists()) {
				dbMapFileDir.mkdirs();
			}
		}

		File dtoFile = new File(dtoDirPath + File.separator + className + ".java");
		if (dtoFile.exists() && !sourcegeneratorProperties.isForceCover()) {
			FileUtils.writeStringToFile(dtoFile, dtoString);
			System.out.println("entify已经存在 ，替换旧文件");
		} else {
			FileUtils.writeStringToFile(dtoFile, dtoString);
			System.out.println("成功生成entify文件");
		}

		File apiInterfaceFile = new File(apiInterfaceDirPath + File.separator + className + "Mapper.java");
		if (apiInterfaceFile.exists() && !sourcegeneratorProperties.isForceCover()) {
			FileUtils.writeStringToFile(apiInterfaceFile, apiInterfaceString);
			System.out.println("Mapper接口已经存在 ，替换旧文件");
		} else {
			FileUtils.writeStringToFile(apiInterfaceFile, apiInterfaceString);
			System.out.println("成功生成Mapper接口文件");
		}
		System.out.printf("OK");
	}

	private String tableNameToIdColumnName(String tableName) {
		return tableName + "_id";
	}

	private String dbNameToPropertyName(String dbName, boolean isCheckHead) {
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < dbName.length(); i++) {
			char c = dbName.charAt(i);
			if (c == '_') {
				i++;
				if (i == dbName.length())
					break;
				c = dbName.charAt(i);
				builder.append(Character.toUpperCase(c));
			} else {
				builder.append(c);
			}
		}
		String returnStr = builder.toString();
		return returnStr;
	}

	private String dbNameToPropertyName(String dbName) {

		return dbNameToPropertyName(dbName, false);
	}

	private String toHeadUpper(String data) {
		return String.valueOf(Character.toUpperCase(data.charAt(0))) + data.substring(1);
	}

	private String toHeadLow(String data) {
		return String.valueOf(Character.toLowerCase(data.charAt(0))) + data.substring(1);
	}

	private String mergeTemplateIntoString(VelocityEngine velocityEngine, String templatePath,
			Map<String, Object> data) {
		StringWriter stringWriter = new StringWriter();
		VelocityContext velocityContext = new VelocityContext(data);
		velocityEngine.mergeTemplate(templatePath, sourcegeneratorProperties.getEncoding(), velocityContext,
				stringWriter);
		return stringWriter.toString();
	}

	private boolean isEmpty(String str) {
		return ((str == null) || (str.length() == 0));
	}
}
